package com.example.decode;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import java.util.Base64;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.function.BiConsumer;
import java.util.function.Function;

public class DecodeApplicatoin {


    static class Calculator {
        Calculator() {
        }

        public static long mod(long val, int bits) {
            return val - (val >> bits << bits);
        }

        public static long mul(long val, int bits) {
            return val << bits;
        }

        public static long div(long val, int bits) {
            return val >> bits;
        }
    }


    public static class FmtSrcDstLen {
        public int code = 0;
        public int src;
        public int dst;
        public int len;
    }

    public static void main(String[] args) {

        int toAppendArrayIndex = 20;
        int toAppendIndexItemOffset = (int)Calculator.mul(Calculator.mod(toAppendArrayIndex, 20), 5);



        ByteBuf buf = Unpooled.wrappedBuffer(Base64.getDecoder().decode(
                "ARFdXrwAAgALAAAIB4PxEMHvj8MJAAAGBYHxEMJEBAAMAAAJCITxEGEwBAEbAQwAAAkIhPEQYVMCATwCDAAACQiE8RBhMwFAWgMKAAAHBoLxEFgA2wEMAAAJCID6KAPB74/kCgAABwaA+igBwmUBAAsAAAgHgPooAlgA/A=="
        ));

        parseKwp(buf);

    }

    public static void parseKwp(ByteBuf buf) {

        int deviceType = buf.readUnsignedByte();
        int configType = buf.readUnsignedByte();
        int version = buf.readInt();

        int groupCnt = buf.readUnsignedByte(); // 组数

        for(int i = 0; i < groupCnt; ++i) {
            int groupId = buf.readUnsignedByte();
            ByteBuf startCommunicationData = buf.readSlice(buf.readUnsignedByte());
            ByteBuf stopCommunicationData = buf.readSlice(buf.readUnsignedByte());

            int dataCnt = buf.readUnsignedByte();
            for(int j= 0; j < dataCnt; ++j) {
                int dataId = buf.readUnsignedByte();

                ByteBuf data = buf.readSlice(buf.readUnsignedByte());
                data.skipBytes(1); // 00
                int total = data.readUnsignedShort(); // total len

                while (data.readableBytes() > 0) {
                    ByteBuf kData = data.readSlice(data.readUnsignedByte());
                    System.out.println(ByteBufUtil.hexDump(kData));
                    FmtSrcDstLen fmt = getData(kData);
                    int ecu = kData.readUnsignedByte();
                    int value = 0;
                    int x1 = 0;
                    int x2 = 0;

                    switch (ecu) {
                        case 0x61: //ReadDataByLocalIdentifier
                            int RLI = kData.readUnsignedByte();
                            switch (RLI) {
                                case 0x30: // Engine Speed
                                    x1 = kData.readUnsignedByte();
                                    x2 = kData.readUnsignedByte();
                                    value = x1 *  256 + x2;
                                    System.out.println(value);
                                    break;
                                case 0x53: // Vehicle Speed
                                    x1 = kData.readUnsignedByte();
                                    x2 = kData.readUnsignedByte();
                                    value = x1 *  256 + x2;
                                    System.out.println(value);
                                    break;
                                case 0x33: // Water Temperature
                                    x1 = kData.readUnsignedByte();
                                    x2 = kData.readUnsignedByte();
                                    value = x1 *  256 + x2 - 40;
                                    System.out.println(value);
                                    break;
                            }
                            break;
                        case 0x58: // ReadStatusOfDiagnosticTroubleCodes
                            int dtcCnt = kData.readUnsignedByte();
                            for(int k = 0; k < dtcCnt; ++k) {
                                x1 = kData.readUnsignedByte();
                                x2 = kData.readUnsignedByte();
                                int s = kData.readUnsignedByte();

                                if((fmt.code & 0x01) != 0) {
                                    if(fmt.dst == 0x10) {
                                        System.out.println(getFaultCode(x1, x2));
                                    } else if(fmt.dst == 0x28) {
                                        System.out.println(String.format("%X%X", x1, x2));
                                    }
                                }
                            }
                            break;
                    }
                }
            }
        }
    }

    public static FmtSrcDstLen getData(ByteBuf kData) {
        FmtSrcDstLen info = new FmtSrcDstLen();
        int fmt = kData.readUnsignedByte();
        int  f0 = (fmt & 0xc0) ;
        if(f0 == 0xc0 || f0 == 0x80) {
            info.src = kData.readUnsignedByte();
            info.dst = kData.readUnsignedByte();
            info.code |= 1;
        }
        if((fmt & 0x3f) == 0) {
            kData.skipBytes(1);
        }



        return info;
    }

    /**
     * 根据c1,c2获取对应的故障码
     *              c1                                                 c2
     *  |bit7|bit6|bit5|bit4|bit3|bit2|bit1|bit0|    |bit7|bit6|bit5|bit4|bit3|bit2|bit1|bit0|
     *  第一步: 取c1的 |bit7|bit6|  <--> (00:P, 01:C, 10:B, 11:U)
     *  第二步: 取c1的 |bit5|bit4|  <--> (0~3)
     *  第三步: 取c1的 |bit3|bit2|bit1|bit0|
     *  第四步: 取c2的值
     *
     * @param c1
     * @param c2
     * @return
     */
    public static String getFaultCode(int c1, int c2) {
        StringBuilder code = new StringBuilder(5);
        switch ((c1 & 0xc0) >>> 6) { // c1: |bit7|bit6|
            case 0b00: code.append('P'); break; // 00
            case 0b01: code.append('C'); break; // 01
            case 0b10: code.append('B'); break; // 10
            case 0b11: code.append('U'); break; // 11
        }
        code.append(String.format("%02x", c1 & 0x3f));
        code.append(String.format("%02x", c2));
        return code.toString();
    }
}
